package com.example.gulimall.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.common.config.rabbit.common.RabbitTemplateWrapper;
import com.example.common.config.rabbit.order.OrderTransactionalConfig;
import com.example.common.config.rabbit.ware.WareTransactionalConfig;
import com.example.common.constant.BizCodeEnum;
import com.example.common.constant.order.OrderConstant;
import com.example.common.constant.order.OrderPayTypeEnum;
import com.example.common.constant.order.OrderStatusEnum;
import com.example.common.entity.member.MemberEntity;
import com.example.common.entity.member.MemberReceiveAddressEntity;
import com.example.common.entity.order.OrderEntity;
import com.example.common.entity.order.OrderItemEntity;
import com.example.common.entity.product.SkuInfoEntity;
import com.example.common.entity.product.SpuInfoEntity;
import com.example.common.exception.NoStockException;
import com.example.common.to.kill.SecKillOrderTo;
import com.example.common.to.order.OrderCreateTo;
import com.example.common.to.ware.FareTo;
import com.example.common.to.ware.SkuHasStockTo;
import com.example.common.to.ware.WareSkuLockTo;
import com.example.common.utils.*;
import com.example.common.vo.cart.CartItemVo;
import com.example.common.vo.order.*;
import com.example.gulimall.order.config.AlipayTemplate;
import com.example.gulimall.order.dao.OrderDao;
import com.example.gulimall.order.entity.PaymentInfoEntity;
import com.example.gulimall.order.feign.CartFeignService;
import com.example.gulimall.order.feign.MemberFeignService;
import com.example.gulimall.order.feign.ProductFeignService;
import com.example.gulimall.order.feign.WmsFeignService;
import com.example.gulimall.order.service.OrderItemService;
import com.example.gulimall.order.service.OrderService;
import com.example.gulimall.order.service.PaymentInfoService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;


@Slf4j
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired private MapperFacade mapperFacade;
    @Autowired private MapperFactory mapperFactory;
    @Autowired private AlipayTemplate alipayTemplate;
    @Autowired private WmsFeignService wmsFeignService;
    @Autowired private StringRedisTemplate redisTemplate;
    @Autowired private OrderItemService orderItemService;
    @Autowired private CartFeignService cartFeignService;
    @Autowired private PaymentInfoService paymentInfoService;
    @Autowired private MemberFeignService memberFeignService;
    @Autowired private ThreadPoolExecutor threadPoolExecutor;
    @Autowired private ProductFeignService productFeignService;
    @Autowired private RabbitTemplateWrapper rabbitTemplateWrapper;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    @SneakyThrows
    public String confirmOrder(Model model) {

        log.info(">>>>>>>>>>>>>>>>>>>>>>>>> 用户确认提交订单 开始 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        // 获取用户当前登录信息
        final MemberEntity member = this.getMember();
        final OrderConfirmVo confirmVo = new OrderConfirmVo();
        confirmVo.setIntegration(member.getIntegration());

        // 重新为当前线程设置请求头
        final RequestAttributes attributes = RequestContextHolder.getRequestAttributes();

        // 根据当前用户查询用户所在的所以收货地址
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(
                () -> {
                    RequestContextHolder.setRequestAttributes(attributes);
                    confirmVo.setAddress(
                            Objects.nonNull(member.getId())
                                    ? this.memberFeignService.getAddress(member.getId())
                                    : Collections.emptyList());
                } , this.threadPoolExecutor
        );
        // 查询用户所有选中的购物项
        CompletableFuture<Void> itemFuture = CompletableFuture.runAsync(
                () -> {
                    RequestContextHolder.setRequestAttributes(attributes);
                    confirmVo.setItems(
                            Objects.nonNull(member.getId())
                                    ? this.cartFeignService.getCurrentUserItem(member.getId())
                                    : Collections.emptyList()
                    );
                }, this.threadPoolExecutor
                // 再次启动异步查询商品库存信息
        ).thenRunAsync(
                () -> {
                    RequestContextHolder.setRequestAttributes(attributes);
                    List<Long> skuIds = confirmVo.getItems().stream()
                            .map(CartItemVo::getSkuId).collect(Collectors.toList());
                    List<SkuHasStockTo> stocks = this.wmsFeignService.hasStock(skuIds);
                    Map<Long, Integer> stockMap = stocks.stream()
                            .collect(Collectors.toMap(SkuHasStockTo::getSkuId, SkuHasStockTo::getStock));

                    List<CartItemVo> stockPost = confirmVo.getItems().stream()
                            .peek(
                                    a -> a.setHasStock((stockMap.get(a.getSkuId()) > 0))
                            ).collect(Collectors.toList());

                    confirmVo.setItems(stockPost);
                }, this.threadPoolExecutor
        );

        String token = putRedis(String.valueOf(member.getId()));
        confirmVo.setOrderToken(token);

        CompletableFuture.allOf(addressFuture, itemFuture).get();

        log.info("查询用户订单信息 : {}", confirmVo);
        log.info("<<<<<<<<<<<<<<<<<<<<<<<< 用户确认提交订单 结束 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");

        model.addAttribute("order", confirmVo);
        return "confirm";
    }

    /**
     * 这里不太适合使用 at 模式，下单方法是高并发方法，所以这里应该使用其他方式的分布式事务提交并发效率
     *
     * 这里将会采用 柔性事务 （可靠消息 + 最终一致性） 最终一致性事务
     *
     * @param vo            提交的订单内容
     * @param model         模型
     * @param attributes    重定向属性设置
     * @return              下一步要去的页面路径
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String submitOrder(OrderSubmitVo vo, Model model, RedirectAttributes attributes) {
        SubmitOrderRespVo resp = new SubmitOrderRespVo();
        model.addAttribute("order", resp);
        // 验证提交的 token
        MemberEntity member = GuLiMallThreadLocalPool.getMember();
        boolean inspect = this.execLuaScript(member, vo.getOrderToken());
        if (!inspect) {
            resp.setCode(BizCodeEnum.ORDER_TOKEN_INSPECT_ERROR.getCode());
            return choosePage(resp, attributes);
        }

        // 1，创建订单信息
        OrderCreateTo order = this.createOrder(vo, member.getId());
        // 2. 验价
        this.validationAmount (vo.getPayPrice(), order.getOrder().getPayAmount(), resp);
        if (resp.getCode() != BizCodeEnum.ORDER_SUCCESS.getCode()) {
            return choosePage(resp, attributes);
        }
        // 3. 保存订单信息, 发送到MQ延时队列
        this.saveOrderAndSend(order.getOrder());
        // 4.远程调用锁定库存
        int stock = this.lockStock(order.getOrder(), order.getItems());
        if (stock == R.DEFAULT_CODE) {
            resp.setOrder(order.getOrder());
        } else {
            resp.setCode(stock);
        }
        // 页面选择
        return choosePage(resp, attributes);
    }

    @Override
    public OrderEntity getOrderStatus(String orderSn) {
        log.info("订单状态查询：{}", orderSn);
        return this.baseMapper.selectOne(
                Wrappers.<OrderEntity>lambdaQuery()
                        .eq(OrderEntity::getOrderSn, orderSn)
        );
    }

    @Override
    public void releaseOrder(OrderEntity msg) {
        // 1. 查询订单是否完成
        OrderEntity entity = this.baseMapper.selectById(msg.getId());

        // 如果订单不存在，那么直接返回，有可能是因为订单业务执行失败导致事务回滚
        if (Objects.isNull(entity)) {
            return;
        }

        // 只有订单经历了超时未支付，那么久直接更新状态即可
        if (entity.getStatus() == OrderStatusEnum.OBLIGATION.getCode()) {
            this.baseMapper.update(null,
                    Wrappers.<OrderEntity>lambdaUpdate()
                            .set(OrderEntity::getStatus, OrderStatusEnum.CANCEL.getCode())
                            .eq(OrderEntity::getId, entity.getId())
            );

            // 直接关闭订单支付能力
            this.alipayTemplate.close(msg.getOrderSn());
        }

        // 订单状态更新完毕后，直接通知当前系统中的库存，让库存进行回滚
        this.rabbitTemplateWrapper.sendAndSaveAsync(OrderTransactionalConfig.ORDER_EXCHANGE,
                WareTransactionalConfig.STOCK_RELEASE_KEY, entity);
    }

    @Override
    public PayVo getPay(String orderSn) {

        OrderEntity entity = this.baseMapper.selectOne(
                Wrappers.<OrderEntity>lambdaQuery()
                        .eq(OrderEntity::getOrderSn, orderSn)
        );

        List<OrderItemEntity> list = this.orderItemService.list(
                Wrappers.<OrderItemEntity>lambdaQuery()
                        .eq(OrderItemEntity::getOrderSn, orderSn)
        );

        PayVo vo = new PayVo();
        vo.setOut_trade_no(orderSn);
        vo.setSubject(list.get(0).getSkuName());
        vo.setTotal_amount(entity.getPayAmount()
                .setScale(2, BigDecimal.ROUND_UP).toEngineeringString());
        vo.setBody(list.get(0).getSkuAttrsVals());

        return vo;
    }


    @Override
    public String aliAsyncNotify(PayAsyncVo vo, HttpServletRequest request) {

        final String fail = "fail";
        try {
            // 获取到返回的结果
//            Map<String, String> para = AliPayUtils.verifySign(request);
//
//            // AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
//            boolean verify = AlipaySignature.rsaCheckV2(para, alipayTemplate.getAlipay_public_key(),
//                    alipayTemplate.getCharset());
//
//            if (!verify) {
//                log.info("支付验签失败！");
//                return fail;
//            }

            // 验签成功，修改订单状态

            String success = this.paySuccess(vo);
            log.info("订单号支付状态：{}", success);
            return success;
        }catch (Exception ex) {
            log.error("支付宝支付发生异常：", ex);
            return fail;
        }
    }

    @Override
    public PageUtils listWithItem(Map<String, Object> params) {
        // 查询当前登录的用户
        MemberEntity users = GuLiMallThreadLocalPool.getMember();
        IPage<List<OrderEntity>> orderEntities = this.baseMapper
                .selectUserOrder (new Query<OrderEntity>().getPage(params), users.getId());
        return new PageUtils(orderEntities);
    }


    /**
     * 远程调用锁定库存
     *
     * @param order     创建的订单信息
     */
    private int lockStock(OrderEntity order, List<OrderItemEntity> items) {
        WareSkuLockTo to = new WareSkuLockTo();
        to.setOrderSn(order.getOrderSn());

        // OrderItemEntity -> CartItemVo
        this.mapperFactory.classMap(OrderItemEntity.class, CartItemVo.class)
                // 这里 skuId 名称相同，所以不需要进行重复书写
                .field("skuQuantity", "count")
                .field("skuName", "title")
                .field("realAmount", "totalPrice")
                .field("skuPrice", "price")
                .field("skuQuantity", "count")
                .byDefault()
                .register();
        to.setLocks(this.mapperFacade.mapAsList(items, CartItemVo.class));
        // 锁定库存的过程
        R lockProcess = this.wmsFeignService.lockWare(to);
        if (lockProcess.getCode() == R.DEFAULT_CODE) {
            return R.DEFAULT_CODE;
        }
        return BizCodeEnum.ORDER_LOCK_STOCK_EXCEPTION.getCode();
    }

    /**
     * 向数据库中保存订单与订单详情
     *
     * @param order     订单项
     */
    public void saveOrderAndSend(OrderEntity order) {
        this.baseMapper.insert(order);
        // 向MQ发送创建成功的 order
        this.rabbitTemplateWrapper.sendAndSaveAsync(OrderTransactionalConfig.ORDER_EXCHANGE,
                OrderTransactionalConfig.DELAY_KEY, order);
        this.orderItemService.saveBatch(order.getItems());
    }

    /**
     * 对比页面提交的金额于代码计算的价格
     *
     * @param submit        提交的金额
     * @param calculate     计算后的金额
     * @param resp          结果对象
     */
    private void validationAmount(BigDecimal submit, BigDecimal calculate, SubmitOrderRespVo resp) {
        double difference = Math.abs(calculate.subtract(submit).doubleValue());
        if (!(difference <= OrderConstant.DEFAULT_DIFFERENCE)) {
            resp.setCode(BizCodeEnum.ORDER_AMOUNT_INSPECT_ERROR.getCode());
        }
    }

    /**
     * 创建订单
     *
     * @param vo        页面提交美容
     * @param memberId  当前登录人
     * @return          订单内容
     */
    private OrderCreateTo createOrder(OrderSubmitVo vo, Long memberId) {
        OrderCreateTo order = new OrderCreateTo();
        // 创建一个订单
        OrderEntity orderEntity = this.buildOrder(vo, memberId);

        // 创建一个订单项
        List<OrderItemEntity> orderItems = this.buildOrderItems(memberId, orderEntity.getOrderSn());
        // 计算所有的价格
        this.computePrice (orderEntity, orderItems);

        order.setOrder(orderEntity);
        order.setItems(orderItems);
        return order;
    }

    /**
     * 计算当前订单的所有价格
     *
     * @param o       订单类
     * @param os      订单项信息
     */
    private void computePrice(OrderEntity o, List<OrderItemEntity> os) {

        for (OrderItemEntity s : os) {
            log.info("");
            // 计算所有的优惠券叠加
            o.setCouponAmount(o.getCouponAmount().add(s.getCouponAmount()))
            // 计算所有的积分抵扣金额
            .setIntegrationAmount(o.getIntegrationAmount().add(s.getIntegrationAmount()))
            // 计算促销优化金额
            .setPromotionAmount(o.getPromotionAmount().add(s.getPromotionAmount()))
            // 订单总额
            .setTotalAmount(o.getTotalAmount().add(s.getRealAmount()))
            // 积分
            .setIntegration(o.getIntegration() + s.getGiftIntegration())
            // 成长值
            .setGrowth( o.getGrowth() + s.getGiftGrowth());
        }

        log.info(">>>>>>>>>>>>>> 商品金额：{} <<<<<<<<<<<<<<<<<<<", o.getTotalAmount());
        log.info(">>>>>>>>>>>>>> 运费金额：{} <<<<<<<<<<<<<<<<<<<", o.getFreightAmount());
        // 实际支付金额，加上运费金额
        o.setPayAmount(o.getTotalAmount().add(o.getFreightAmount()));
        log.info(">>>>>>>>>>>>>> 实际金额：{} <<<<<<<<<<<<<<<<<<<", o.getPayAmount());
    }

    /**
     * 创建订单
     *
     * @param vo        提交参数
     * @param memberId  当前用户
     * @return          订单信息
     */
    private OrderEntity buildOrder(OrderSubmitVo vo, Long memberId) {
        OrderEntity orderEntity = new OrderEntity();
        // 生成订单号
        orderEntity.setOrderSn(IdWorker.getTimeId());

        // 远程查询运费，收货地址信息
        R fare = this.wmsFeignService.getFare(vo.getAddrId());
        FareTo fareTo = fare.getData("data", FareTo.class);

        // 设置运费
        MemberReceiveAddressEntity address = fareTo.getAddress();
        return orderEntity
                .setMemberId(memberId)
                .setCreateTime(new Date())
                .setModifyTime(new Date())
                .setFreightAmount(fareTo.getFare())
                .setReceiverCity(address.getCity())
                .setReceiverName(address.getName())
                .setReceiverPhone(address.getPhone())
                .setReceiverRegion(address.getRegion())
                .setReceiverPostCode(address.getPostCode())
                .setReceiverProvince(address.getProvince())
                .setStatus(OrderStatusEnum.OBLIGATION.getCode())
                .setReceiverDetailAddress(address.getDetailAddress())
                .setAutoConfirmDay(OrderConstant.DEFAULT_AUTO_CONFIRM_DAY);
    }

    public BigDecimal realAmount (OrderItemEntity e){
        // 当前订单实际金额
        BigDecimal actual = e.getSkuPrice()
                .multiply(BigDecimal.valueOf(e.getSkuQuantity()));
        return actual.subtract(e.getCouponAmount())
                .subtract(e.getPromotionAmount())
                .subtract(e.getIntegrationAmount());
    }

    /**
     * 获取订单项信息
     *
     * @param memberId  当前登录人
     * @param orderSn   订单号
     * @return          订单项信息
     */
    private List<OrderItemEntity> buildOrderItems(Long memberId, String orderSn) {
        List<CartItemVo> cartItem = this.cartFeignService.getCurrentUserItem(memberId);

        List<Long> skuIds = cartItem.stream().map(CartItemVo::getSkuId).collect(Collectors.toList());
        List<SpuInfoEntity> spuInfo = this.productFeignService.getSpuInfoBySkuIds(skuIds);

        Map<Long, SpuInfoEntity> spuMap = spuInfo.stream()
                .collect(Collectors.toMap(SpuInfoEntity::getSkuId, Function.identity()));

        return cartItem.stream().map(
                a -> {
                    OrderItemEntity entity = new OrderItemEntity()
                            .setOrderSn(orderSn)
                            .setSkuId(a.getSkuId())
                            .setSkuPic(a.getImage())
                            .setSkuName(a.getTitle())
                            .setSkuPrice(a.getPrice())
                            .setSkuQuantity(a.getCount())
                            .setCouponAmount(BigDecimal.ZERO)
                            .setPromotionAmount(BigDecimal.ZERO)
                            .setIntegrationAmount(BigDecimal.ZERO)
                            .setSpuId(spuMap.get(a.getSkuId()).getId())
                            .setSpuName(spuMap.get(a.getSkuId()).getSpuName())
                            .setCategoryId(spuMap.get(a.getSkuId()).getCatalogId())
                            .setSkuAttrsVals(String.join(";", a.getSkuAttr()))
                            .setSpuBrand(String.valueOf(spuMap.get(a.getSkuId()).getBrandId()))
                            // 积分   成长值
                            .setGiftGrowth(a.getPrice().multiply(BigDecimal.valueOf(a.getCount())).intValue())
                            .setGiftIntegration(a.getPrice().multiply(BigDecimal.valueOf(a.getCount())).intValue());
                    // 计算实际金额
                    return entity.setRealAmount(this.realAmount(entity));
                }
        ).collect(Collectors.toList());
    }

    /**
     * 原子操作订单令牌
     *
     * @param member        当前登录用户
     * @param orderToken    当前用户提交的令牌
     * @return              验证结果
     */
    private boolean execLuaScript(MemberEntity member, String orderToken) {
        String key = OrderConstant.USER_ORDER_TOKEN_PREFIX.concat(String.valueOf(member.getId()));

        String script = LuaScriptUtil.atomicOrderToken();
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);

        Long executeResult = this.redisTemplate.execute(
                redisScript,
                Collections.singletonList(key),
                orderToken
        );

        // 1: success  other: failure
        return (null != executeResult) && executeResult == 1L;
    }


    public MemberEntity getMember () {
        return GuLiMallThreadLocalPool.getMember();
    }

    public String putRedis (String suffix) {
        String token = GeneratorUtils.orderToken();
        String key = OrderConstant.USER_ORDER_TOKEN_PREFIX.concat(suffix);
        this.redisTemplate.opsForValue().set(key, token,
                OrderConstant.USER_ORDER_TOKEN_TIMEOUT, TimeUnit.MINUTES);
        return token;
    }



    public String choosePage (SubmitOrderRespVo resp, RedirectAttributes attributes) {

        int code = resp.getCode();
        BizCodeEnum[] values = BizCodeEnum.values();
        for (BizCodeEnum value : values) {
            if (value.getCode() == code) {
                resp.setMessage(value.getMsg());
                break;
            }
        }

        if (code == 0) {
            return "pay";
        } else {
            log.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
            log.error("重定向页面携带消息内容：{}", resp.getMessage());
            log.info("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
            attributes.addFlashAttribute("msg", resp.getMessage());
            return "redirect:http://order.gulimall.com/toTrade";
        }
    }

    /**
     * 支付成功后的处理
     *
     * @param vo    商户系统订单号
     * @return      fail / success
     */
    private String paySuccess(PayAsyncVo vo) {

        OrderEntity orderEntity = this.baseMapper.selectOne(
                Wrappers.<OrderEntity>lambdaQuery()
                        .eq(OrderEntity::getOrderSn, vo.getOut_trade_no())
        );

        if (Objects.isNull(orderEntity)) {
            log.error("订单号对应的订单不存在：{}", vo.getOut_trade_no());
            return "fail";
        }

        orderEntity.setStatus(OrderStatusEnum.PAYED.getCode())
                .setPayType(OrderPayTypeEnum.A_LI_PAY.getCode());

        this.createInsertPaymentInfo (vo);
        return "success";
    }


    public void createInsertPaymentInfo (PayAsyncVo vo) {
        this.mapperFactory.classMap(PayAsyncVo.class, PaymentInfoEntity.class)
                .field("trade_no", "alipayTradeNo")
                .field("out_trade_no", "orderSn")
                .field("trade_status", "paymentStatus")
                .field("notify_time", "callbackTime")
                .byDefault()
                .register();
        PaymentInfoEntity entity = this.mapperFacade.map(vo, PaymentInfoEntity.class);
        this.paymentInfoService.save(entity);
    }

    // *************************** 秒杀创建订单业务代码 ************************** //

    @Override
    public void createSecKillOrder(SecKillOrderTo to) {
        // 1. 生成订单
        MemberReceiveAddressEntity address = this.getDefaultAddress(to.getMemberId());
        OrderEntity order = this.buildSecKillOrder(address, to.getMemberId(), to.getOrderSn());

        // 2. 创建订单项
        OrderItemEntity itemEntities = this.buildSecKillOrderItems(to);

        // 3. 根据商品id扣减库存
        int stock = lockStock(order, Collections.singletonList(itemEntities));
        if (stock == R.DEFAULT_CODE) {
            this.saveOrderAndSend(order);
        }
    }

    /**
     * 构建出订单信息
     *
     * @param address   用户默认地址
     * @param memberId  用户ID
     * @param orderSn   订单号
     * @return          订单实体
     */
    private OrderEntity buildSecKillOrder (MemberReceiveAddressEntity address,
                                            Long memberId, String orderSn) {
        OrderEntity orderEntity = new OrderEntity();
        // 生成订单号
        orderEntity.setOrderSn(orderSn);
        // 设置运费
        return orderEntity
                .setMemberId(memberId)
                .setCreateTime(new Date())
                .setModifyTime(new Date())
                .setReceiverCity(address.getCity())
                .setReceiverName(address.getName())
                .setReceiverPhone(address.getPhone())
                .setReceiverRegion(address.getRegion())
                .setReceiverPostCode(address.getPostCode())
                .setReceiverProvince(address.getProvince())
                .setStatus(OrderStatusEnum.OBLIGATION.getCode())
                .setReceiverDetailAddress(address.getDetailAddress())
                .setFreightAmount(FareUtils.getFare(address.getPhone()))
                .setAutoConfirmDay(OrderConstant.DEFAULT_AUTO_CONFIRM_DAY);
    }

    /**
     * 获取当前用户默认地址
     *
     * @param memberId      用户ID
     * @return              默认地址信息
     */
    private MemberReceiveAddressEntity getDefaultAddress (Long memberId) {
        List<MemberReceiveAddressEntity> address = this.memberFeignService.getAddress(memberId);
        return address.stream().filter(a -> a.getDefaultStatus() == 1).findFirst().get();
    }

    /**
     * 创建秒杀订单项
     *
     * @param to    秒杀订单信息
     * @return      订单项
     */
    private OrderItemEntity buildSecKillOrderItems (SecKillOrderTo to) {
        SkuInfoEntity skuInfoEntity = this.productFeignService.getSkuInfo(to.getSkuId());
        List<SpuInfoEntity> skuIds = this.productFeignService
                .getSpuInfoBySkuIds(Collections.singletonList(to.getSkuId()));
        SpuInfoEntity spuInfoEntity = skuIds.get(0);

        return new OrderItemEntity()
                .setOrderSn(to.getOrderSn())
                .setSkuId(to.getSkuId())
                .setSkuPic(skuInfoEntity.getSkuDefaultImg())
                .setSkuName(skuInfoEntity.getSkuName())
                .setSkuPrice(skuInfoEntity.getPrice())
                .setSkuQuantity(to.getNum())
                .setCouponAmount(BigDecimal.ZERO)
                .setPromotionAmount(BigDecimal.ZERO)
                .setIntegrationAmount(BigDecimal.ZERO)
                .setSpuId(spuInfoEntity.getId())
                .setSpuName(spuInfoEntity.getSpuName())
                .setCategoryId(spuInfoEntity.getCatalogId())
                .setSpuBrand(spuInfoEntity.getBrandId().toString())
                // 积分   成长值
                .setGiftGrowth(skuInfoEntity.getPrice().multiply(BigDecimal.valueOf(to.getNum())).intValue())
                .setGiftIntegration(skuInfoEntity.getPrice().multiply(BigDecimal.valueOf(to.getNum())).intValue());
    }
}